import {
  Injectable,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Playlist } from './playlist.entity';
import {
  CreatePlaylistDto,
  UpdatePlaylistDto,
  PlaylistResponseDto,
  PlaylistWithSongsDto,
} from './playlist.dto';
import { User } from '../user/user.entity';
import { Song } from '../song/song.entity';

/**
 * 播放列表服务
 * 提供播放列表相关的所有业务逻辑
 */
@Injectable()
export class PlaylistService {
  /**
   * 构造函数，注入所需的Repository
   * @param playlistRepository 播放列表仓库
   * @param userRepository 用户仓库
   * @param songRepository 歌曲仓库
   */
  constructor(
    @InjectRepository(Playlist)
    private playlistRepository: Repository<Playlist>,
    @InjectRepository(User) private userRepository: Repository<User>,
    @InjectRepository(Song) private songRepository: Repository<Song>,
  ) {}

  /**
   * 创建新的播放列表
   * @param createPlaylistDto 创建播放列表的数据
   * @param userId 创建者ID
   * @returns 创建的播放列表响应数据
   */
  async createPlaylist(
    createPlaylistDto: CreatePlaylistDto,
    userId: number,
  ): Promise<PlaylistResponseDto> {
    // 验证用户是否存在
    console.log(userId);
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user || !userId) {
      throw new NotFoundException('用户不存在');
    }

    // 创建新播放列表
    const playlist = this.playlistRepository.create({
      ...createPlaylistDto,
      createdBy: user,
    });

    // 如果有歌曲ID，添加歌曲到播放列表
    if (createPlaylistDto.songIds && createPlaylistDto.songIds.length > 0) {
      const songs = await this.songRepository.findByIds(
        createPlaylistDto.songIds,
      );
      if (songs.length !== createPlaylistDto.songIds.length) {
        throw new BadRequestException('部分歌曲不存在');
      }
      playlist.songs = songs;
    }

    // 保存到数据库
    await this.playlistRepository.save(playlist);

    // 转换为响应DTO并返回
    return this.mapPlaylistToResponseDto(playlist);
  }

  /**
   * 获取官方推荐播放列表
   * @param limit 限制返回的播放列表数量，默认10
   * @returns 官方推荐播放列表响应数据数组
   */
  async findOfficialPlaylists(
    limit: number = 10,
  ): Promise<PlaylistResponseDto[]> {
    const query = this.playlistRepository
      .createQueryBuilder('playlist')
      .leftJoinAndSelect('playlist.createdBy', 'createdBy')
      .loadRelationCountAndMap('playlist.songsCount', 'playlist.songs')
      .where('playlist.isOfficial = :isOfficial', { isOfficial: true })
      .orderBy('playlist.recommendLevel', 'DESC')
      .take(limit);

    const playlists = await query.getMany();
    return playlists.map((playlist) => this.mapPlaylistToResponseDto(playlist));
  }

  /**
   * 获取播放列表列表
   * @param userId 可选的用户ID，用于筛选特定用户的播放列表
   * @returns 播放列表响应数据数组
   */
  async findAllPlaylists(userId?: number): Promise<PlaylistResponseDto[]> {
    const query = this.playlistRepository
      .createQueryBuilder('playlist')
      .leftJoinAndSelect('playlist.createdBy', 'createdBy')
      .loadRelationCountAndMap('playlist.songsCount', 'playlist.songs');

    // 如果提供了用户ID，只返回该用户创建的播放列表
    if (userId) {
      query.andWhere('playlist.createdBy.id = :userId', { userId });
    } else {
      // 否则，返回所有公开的播放列表
      query.andWhere('playlist.isPublic = :isPublic', { isPublic: true });
    }

    // 按创建时间倒序排列
    query.orderBy('playlist.createdAt', 'DESC');

    const playlists = await query.getMany();
    return playlists.map((playlist) => this.mapPlaylistToResponseDto(playlist));
  }

  /**
   * 根据ID查找播放列表
   * @param id 播放列表ID
   * @param includeSongs 是否包含歌曲详情
   * @returns 播放列表响应数据，可能包含歌曲列表
   */
  async findPlaylistById(
    id: number,
    includeSongs: boolean = false,
  ): Promise<PlaylistResponseDto | PlaylistWithSongsDto> {
    const query = this.playlistRepository
      .createQueryBuilder('playlist')
      .leftJoinAndSelect('playlist.createdBy', 'createdBy')
      .where('playlist.id = :id', { id });

    // 根据参数决定是否包含歌曲详情
    if (includeSongs) {
      query
        .leftJoinAndSelect('playlist.songs', 'songs')
        .leftJoinAndSelect('songs.artist', 'artist')
        .leftJoinAndSelect('songs.album', 'album');
    } else {
      query.loadRelationCountAndMap('playlist.songsCount', 'playlist.songs');
    }

    const playlist = await query.getOne();
    if (!playlist) {
      throw new NotFoundException('播放列表不存在');
    }

    // 根据是否包含歌曲详情返回不同的DTO
    if (includeSongs) {
      return this.mapPlaylistWithSongsToDto(playlist);
    }

    return this.mapPlaylistToResponseDto(playlist);
  }

  /**
   * 更新播放列表信息
   * @param id 播放列表ID
   * @param updatePlaylistDto 更新的播放列表数据
   * @returns 更新后的播放列表响应数据
   */
  async updatePlaylist(
    id: number,
    updatePlaylistDto: UpdatePlaylistDto,
  ): Promise<PlaylistResponseDto> {
    const playlist = await this.playlistRepository.findOne({
      where: { id },
      relations: ['createdBy'],
    });

    if (!playlist) {
      throw new NotFoundException('播放列表不存在');
    }

    // 更新播放列表信息
    Object.assign(playlist, updatePlaylistDto);
    await this.playlistRepository.save(playlist);

    return this.mapPlaylistToResponseDto(playlist);
  }

  /**
   * 删除播放列表
   * @param id 播放列表ID
   */
  async deletePlaylist(id: number): Promise<void> {
    const result = await this.playlistRepository.delete(id);
    if (result.affected === 0) {
      throw new NotFoundException('播放列表不存在');
    }
  }

  /**
   * 向播放列表添加歌曲
   * @param playlistId 播放列表ID
   * @param songId 歌曲ID
   * @returns 更新后的包含歌曲的播放列表响应数据
   */
  async addSongToPlaylist(
    playlistId: number,
    songId: number,
  ): Promise<PlaylistWithSongsDto> {
    const playlist = await this.playlistRepository.findOne({
      where: { id: playlistId },
      relations: ['createdBy', 'songs'],
    });

    if (!playlist) {
      throw new NotFoundException('播放列表不存在');
    }

    const song = await this.songRepository.findOne({ where: { id: songId } });
    if (!song) {
      throw new NotFoundException('歌曲不存在');
    }

    // 检查歌曲是否已在播放列表中
    const isSongInPlaylist = playlist.songs.some((s) => s.id === songId);
    if (isSongInPlaylist) {
      throw new BadRequestException('歌曲已在播放列表中');
    }

    // 添加歌曲到播放列表
    playlist.songs.push(song);
    await this.playlistRepository.save(playlist);

    return this.mapPlaylistWithSongsToDto(playlist);
  }

  /**
   * 从播放列表移除歌曲
   * @param playlistId 播放列表ID
   * @param songId 歌曲ID
   * @returns 更新后的包含歌曲的播放列表响应数据
   */
  async removeSongFromPlaylist(
    playlistId: number,
    songId: number,
  ): Promise<PlaylistWithSongsDto> {
    const playlist = await this.playlistRepository.findOne({
      where: { id: playlistId },
      relations: ['createdBy', 'songs'],
    });

    if (!playlist) {
      throw new NotFoundException('播放列表不存在');
    }

    // 过滤掉要移除的歌曲
    playlist.songs = playlist.songs.filter((song) => song.id !== songId);
    await this.playlistRepository.save(playlist);

    return this.mapPlaylistWithSongsToDto(playlist);
  }

  /**
   * 切换播放列表点赞状态
   * @param playlistId 播放列表ID
   * @returns 点赞数和点赞状态
   * @note 简化实现，实际项目中需要维护用户的点赞记录
   */
  async toggleLike(
    playlistId: number,
  ): Promise<{ likes: number; isLiked: boolean }> {
    const playlist = await this.playlistRepository.findOne({
      where: { id: playlistId },
    });
    if (!playlist) {
      throw new NotFoundException('播放列表不存在');
    }

    // 增加点赞数
    playlist.likes += 1;
    await this.playlistRepository.save(playlist);

    // 这里简化处理，实际项目中需要维护用户的点赞记录
    return { likes: playlist.likes, isLiked: true };
  }

  /**
   * 将播放列表实体映射为响应DTO
   * @param playlist 播放列表实体
   * @returns 播放列表响应DTO
   */
  private mapPlaylistToResponseDto(playlist: any): PlaylistResponseDto {
    const {
      id,
      name,
      description,
      coverImageUrl,
      createdBy,
      songsCount,
      likes,
      isPublic,
      isOfficial,
      recommendLevel,
      createdAt,
      updatedAt,
    } = playlist;
    return {
      id,
      name,
      description,
      coverImageUrl,
      createdBy: { id: createdBy.id, username: createdBy.username },
      songCount: songsCount,
      likes,
      isPublic,
      isOfficial,
      recommendLevel,
      createdAt,
      updatedAt,
    };
  }

  /**
   * 将播放列表实体映射为包含歌曲的响应DTO
   * @param playlist 播放列表实体
   * @returns 包含歌曲的播放列表响应DTO
   */
  private mapPlaylistWithSongsToDto(playlist: Playlist): PlaylistWithSongsDto {
    const {
      id,
      name,
      description,
      coverImageUrl,
      createdBy,
      songs,
      likes,
      isPublic,
      isOfficial,
      recommendLevel,
      createdAt,
      updatedAt,
    } = playlist;
    return {
      id,
      name,
      description,
      coverImageUrl,
      createdBy: { id: createdBy.id, username: createdBy.username },
      songCount: songs.length,
      likes,
      isPublic,
      isOfficial,
      recommendLevel,
      createdAt,
      updatedAt,
      songs: songs.map((song) => ({
        id: song.id,
        title: song.title,
        artist: song.artist
          ? { id: song.artist.id, name: song.artist.name }
          : { id: 0, name: '未知歌手' },
        album: song.album
          ? { id: song.album.id, title: song.album.title }
          : { id: 0, title: '未知专辑' },
        duration: song.duration,
        coverImageUrl: song.album?.coverImageUrl || song.coverImageUrl,
        audioUrl: song.audioUrl,
      })),
    };
  }
}
